#ifndef EVECTOROPS1_HPP_
#define EVECTOROPS1_HPP_

/* The following code example is taken from the book
 * "C++ Templates - The Complete Guide"
 * by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002
 *
 * (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002.
 * Permission to copy, use, modify, sell and distribute this software
 * is granted provided this copyright notice appears in all copies.
 * This software is provided "as is" without express or implied
 * warranty, and with no claim as to its suitability for any purpose.
 */
/* modified by ofgr 21.03.2008 */

#include <stddef.h>
#include <cassert>
#include "usertypes.hpp"

//***************************************************************************

/* helper traits class to select how to refer to an 'expression template node' 
 * - in general: by reference
 * - for scalars: by value
 */

//template <typename T> class A_Scalar;

// primary template
template <typename T>
class A_Traits 
{
public:
  typedef T const& ExprRef;     // type to refer to is constant reference
};

// partial specialization for scalars
template <typename T>
class A_Traits<A_Scalar<T> > 
{
public:
  typedef A_Scalar<T> ExprRef;  // type to refer to is ordinary value
};

//***************************************************************************

// class for objects that represent the addition of two operands
template <typename T, typename OP1, typename OP2>
class A_Add 
{
private:
  typename A_Traits<OP1>::ExprRef op1;    // first operand
  typename A_Traits<OP2>::ExprRef op2;    // second operand

public: 
  // constructor initializes references to operands
  A_Add (OP1 const& a, OP2 const& b)
    : op1(a), op2(b) { }

  // compute sum when value requested
  T operator[] (myindex idx) const 
  {
    return op1[idx] + op2[idx];
  }

  // size is maximum size
  myindex size() const 
  {
    //assert (op1.size()==0 || op2.size()==0 || op1.size()==op2.size());
    return op1.size()!=0 ? op1.size() : op2.size();
  }
};


// class for objects that represent the keeping of sign
template <typename T, typename OP2>
class A_Plus
{
private:
  typename A_Traits<OP2>::ExprRef op2;    // only one operand

public: 
  // constructor initializes references to operands
  A_Plus (OP2 const& b)
    : op2(b) { }

  // compute sum when value requested
  T operator[] (myindex idx) const 
  {
    return op2[idx];
  }

  // size is maximum size
  myindex size() const 
  {
    return op2.size();
  }
};

// class for objects that represent the subtraction of two operands
template <typename T, typename OP1, typename OP2>
class A_Sub
{
private:
  typename A_Traits<OP1>::ExprRef op1;    // first operand
  typename A_Traits<OP2>::ExprRef op2;    // second operand

public: 
  // constructor initializes references to operands
  A_Sub (OP1 const& a, OP2 const& b)
    : op1(a), op2(b) { }

  // compute sum when value requested
  T operator[] (myindex idx) const 
  {
    return op1[idx] - op2[idx];
  }

  // size is maximum size
  myindex size() const 
  {
    //assert (op1.size()==0 || op2.size()==0 || op1.size()==op2.size());
    return op1.size()!=0 ? op1.size() : op2.size();
  }
};


// class for objects that represent the changing of sign
template <typename T, typename OP2>
class A_Minus
{
private:
  typename A_Traits<OP2>::ExprRef op2;    // only one operand

public: 
  // constructor initializes references to operands
  A_Minus (OP2 const& b)
    : op2(b) { }

  // compute sum when value requested
  T operator[] (myindex idx) const 
  {
    return - op2[idx];
  }

  // size is maximum size
  myindex size() const 
  {
    return op2.size();
  }
};


// class for objects that represent the multiplication of two operands
template <typename T, typename OP1, typename OP2>
class A_Mult 
{
private:
  typename A_Traits<OP1>::ExprRef op1;    // first operand
  typename A_Traits<OP2>::ExprRef op2;    // second operand

public:
  // constructor initializes references to operands
  A_Mult (OP1 const& a, OP2 const& b)
    : op1(a), op2(b) { }

  // compute product when value requested
  T operator[] (myindex idx) const 
  {
    return op1[idx] * op2[idx];
  }

  // size is maximum size
  myindex size() const 
  {
    //assert (op1.size()==0 || op2.size()==0 || op1.size()==op2.size());
    return op1.size()!=0 ? op1.size() : op2.size();
  }
};


#endif //EVECTOROPS1_HPP_
